﻿using System;
using System.Collections.Generic;
using System.Text;
using NExt.Handler.Base.Common.Cache;
using NExt.Handler.Base.Default;
using System.Reflection;
using System.Web;
using System.IO;
using NExt.Handler.Component.Common;
using NExt.Handler.Base.Common.Manager;
using NExt.Serialization;

namespace NExt.Handler.Component.Default.RemoteMethod
{
    public class InstanceMethods
    {
        public MethodInfo MethodInfo { get; set; }
        public object Instance { get; set; }
    }

    public class ResultContentBuilder : ConcurrentCache<ResultContentBuilder, string, InstanceMethods>, IContentBuilder
    {
        public string Build(HttpContext context)
        {
            string fileName = Path.GetFileNameWithoutExtension(context.Request.Path);
            string typeName = context.Request.Headers[HeaderKey.CLASS_TYPE];
            string methodName = context.Request.Headers[HeaderKey.CLASS_METHOD];
            string parametersString = context.Request.Headers[HeaderKey.CLASS_METHOD_PARAMETER];
            InstanceMethods instanceMethods = GetMethod(typeName, fileName, methodName);

            string content = BuildContent(instanceMethods, parametersString);
            return content;
        }

        private InstanceMethods GetMethod(string typeName, string fileName, string methodName)
        {
            string key = string.Format("{0}.{1}.{2}", typeName, fileName, methodName);
            InstanceMethods instanceMethods = GetOrAdd(key, () =>
            {
                Type type = TypeManager.GetType(fileName, typeName);
                object instance = InstanceManager.GetInstance(fileName, typeName);
                MethodInfo methodInfo = type.GetMethod(methodName);
                return new InstanceMethods() { Instance = instance, MethodInfo = methodInfo };
            });

            return instanceMethods;
        }

        private string BuildContent(InstanceMethods instanceMethods, string parametersString)
        {
            object instance = instanceMethods.Instance;
            MethodInfo methodInfo = instanceMethods.MethodInfo;
            ParameterInfo[] parameterInfos = methodInfo.GetParameters();
            IList<string> parameterList = SplitParameters(parametersString);
            object[] deserializedParameters = new object[parameterList.Count];
            for (int i = 0; i < deserializedParameters.Length; i++)
            {
                Type parameterType = parameterInfos[i].ParameterType;
                string parameterValue = parameterList[i];
                deserializedParameters[i] = JsonSerializer.Deserialize(parameterValue, parameterType);
            }

            object result = methodInfo.Invoke(instance, deserializedParameters);
            string content = JsonSerializer.Serialize(result);
            return content;
        }

        //todo : refactor it !!
        //dirty implementation, but it's the nature of string manipulation :(
        //possible to remove method to another class ? [{{}fg},1,2]
        private static IList<string> SplitParameters(string parametersString)
        {
            if (parametersString.Length <= 2)
                return new string[0];

            const char curlyBraceOpen = '{';
            const char curlyBraceClose = '}';

            //remove first and last characters (array open and close chars)
            parametersString = parametersString.Remove(0, 1);
            parametersString = parametersString.Remove(parametersString.Length - 1, 1);

            IList<string> parameters = new List<string>();
            StringBuilder stringBuilder = new StringBuilder();
            bool isParsingObject = false;
            int totalOpenedCurlyBraces = 0;
            for (int i = 0; i < parametersString.Length; i++)
            {
                char currentChar = parametersString[i];

                if (currentChar == curlyBraceOpen)
                {
                    totalOpenedCurlyBraces += 1;
                    isParsingObject = true;
                }

                if (currentChar == curlyBraceClose)
                {
                    if (totalOpenedCurlyBraces == 1)
                    {
                        isParsingObject = false;
                    }
                    else
                    {
                        totalOpenedCurlyBraces -= 1;
                    }
                }

                if (currentChar == ',' && !isParsingObject)
                {
                    string parameter = stringBuilder.ToString();
                    parameters.Add(parameter);
                    stringBuilder.Remove(0, stringBuilder.Length);
                    continue;
                }

                if (i == parametersString.Length - 1)
                {
                    stringBuilder.Append(currentChar);
                    string parameter = stringBuilder.ToString();
                    parameters.Add(parameter);
                    stringBuilder.Remove(0, stringBuilder.Length);
                    continue;
                }

                stringBuilder.Append(currentChar);
            }

            return parameters;
        }
    }
}
